package com.hxk.juc.threadPool;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 虽然juc线程池工具类提供了常用的3种获取线程池的方式，
 * 但在现实业务场景中，不能使用，因为他们底层都是使用LinkedBlockingQueue阻塞队列实现的，
 * 说LinkedBlockingQueue是有界对列，但实际他的有界值为Int的maxVal，所以跟没界一样
 * 其实在实际的使用过程中，都是通过new ThreadPoolExecutor来自定义线程池。
 *  ThreadPoolExecutor是java提供的创建线程的自己定义类，有7大参数：
 *      new ThreadPoolExecutor
 *      (
 *       int corePoolSize, 线程池中常驻核心线程数
         int maximumPoolSize, 线程池能够允许存放的最大线程数
         long keepAliveTime, 空闲线程存活时间
         TimeUnit unit, 存活时间的单位
         BlockingQueue<Runnable> workQueue, 存放已提交但未执行的任务阻塞队列
         ThreadFactory threadFactory, 构造线程池的工厂类
         RejectedExecutionHandler handler， 当线程池最大数量和未执行的任务队列都满后，使用的拒绝策略
        )
    第7个值：拒绝策略，java提供了四种：
        1、AbortPolicy：默认策略，当线程池最大数量和未执行的任务队列都满后，直接抛异常
        2、CallerRunsPolicy：原路打回策略，当线程池最大数量和未执行的任务队列都满后，在进入的线程，退回给线程池调度者去执行
        3、DiscardOldestPolicy：放弃等待最长策略，当线程池最大数量和未执行的任务队列都满后，在进入的线程，放弃等待最长线程
        4、DiscardPolicy：直接放弃策略，当线程池最大数量和未执行的任务队列都满后，在进入的线程，直接放弃
    本Case将通过模拟一个银行办理业务的过程，银行的最大业务办理窗口5个，
        今天实际开通办理业务窗口2个，等候区可等待人数3个，现分别模拟8个人和10个人来办理业务的场景

 */
public class ThreadPoolExecutorCase {

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,//今天实际开通办理业务窗口2个
                5,//银行的最大业务办理窗口5个
                2L,//每2秒钟检查现在的业务办理量
                TimeUnit.SECONDS,//检查时间单位
                new LinkedBlockingQueue(3),//等候区可等待人数3个
                Executors.defaultThreadFactory(),//默认的银行装修和摆设风格，修建银行
                new ThreadPoolExecutor.AbortPolicy()//默认异常策略，要想让拒绝策略起效的阀值=maximumPoolSize+BlockingQueue的容量
                //new ThreadPoolExecutor.CallerRunsPolicy() //本例中的9和10会被main线程执行
                //new ThreadPoolExecutor.DiscardOldestPolicy()//只执行拒绝策略起效的阀值内的线程
                //new ThreadPoolExecutor.DiscardPolicy()//只执行拒绝策略起效的阀值内的线程
        );
        /**
         * 同时来8个人办理业务时：
         *      线程池会动态扩容扩到银行的最大业务办理窗口5个，并且会直接办理那些在等候区没有等候的客户，让等待的人继续等着
         * 同时来10个人办理业务时：
         *      因为10个人已经超过了【拒绝策略起效的阀值=maximumPoolSize+BlockingQueue的容量】，这里会使用拒绝策略：
         *          在AbortPolicy策略，直接抛异常
         *          在CallerRunsPolicy策略，将会把最后的9和10线程（这里是理想状态，线程的调度不是咱们说了算，所以这里不一定是9和10，说不定是2和8等等可能性）退回线程池的调度者Main线程，让main线程去处理
         *          在DiscardOldestPolicy策略，放弃等待最长线程（本例中就是在候客区等待最长的客户）
         *          在DiscardPolicy策略，直接放弃，本例中就是把9和10线程（理想状态），直接说我们银行的业务今天满了，不在办理了。
         */
        try {
            for (int i = 1; i <= 10; i++) {
                final int temp = i;
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"\t 客户"+temp+"办理业务");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPoolExecutor.shutdown();//释放线程
        }
    }
}
